﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using ProLinq.Toolkit;

namespace ProLinq.Projection.Visitors.Optimization
{
	/// <summary>
	/// Adds Where clause to an expression with all possible filters which was not processed
	/// </summary>
	internal class OptimizationVisitor : ExpressionVisitor
	{
		#region Properties

		public Expression OriginalExpression { get; set; }
		public Expression OriginalExpressionProcessedPart { get; set; }
		public IEnumerable<Configuration.Projection> Projections { get; set; }

		#endregion

		#region Methods

		public override Expression Visit(Expression node)
		{
			// 1. create filter expression
			var filterBuilder = new FilterBuilderVisitor
				{
					ProcessedExpressionPart = this.OriginalExpressionProcessedPart
				};

			filterBuilder.Visit(this.OriginalExpression);
			Expression logicalExpression = filterBuilder.FilterExpression;

			// 2. normalize negations
			logicalExpression = new LogicalExpressionVisitor {RemoveNegation = true}.Visit(logicalExpression);

			// 3. project it
			var reducer = new FilterReducerVisitor {Projections = this.Projections};
			logicalExpression = reducer.Visit(logicalExpression);

			// 4. Create Where call
			Type enumType = node.Type.GetEnumerableType();
			return Expression.Call(typeof (Queryable), "Where", new[] {enumType}, node, logicalExpression);
		}

		#endregion
	}
}